/**
 * <copyright>
 * </copyright>
 *
 * $Id$
 */
package spynx.desl.impl;

import java.lang.reflect.Array;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;

import spynx.desl.DeslPackage;
import spynx.desl.Expression;
import spynx.desl.LoopStatement;
import spynx.desl.Statement;
import spynx.desl.runtime.IExecutionContext;
import spynx.desl.runtime.IVariable;

/**
 * <!-- begin-user-doc -->
 * An implementation of the model object '<em><b>Loop Statement</b></em>'.
 * <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * <ul>
 *   <li>{@link spynx.desl.impl.LoopStatementImpl#getCondition <em>Condition</em>}</li>
 *   <li>{@link spynx.desl.impl.LoopStatementImpl#getPostStatement <em>Post Statement</em>}</li>
 *   <li>{@link spynx.desl.impl.LoopStatementImpl#getInitStatement <em>Init Statement</em>}</li>
 *   <li>{@link spynx.desl.impl.LoopStatementImpl#getBodyStatement <em>Body Statement</em>}</li>
 *   <li>{@link spynx.desl.impl.LoopStatementImpl#getLoopType <em>Loop Type</em>}</li>
 *   <li>{@link spynx.desl.impl.LoopStatementImpl#getControlVar <em>Control Var</em>}</li>
 * </ul>
 * </p>
 *
 * @generated
 */
public class LoopStatementImpl extends StatementImpl implements LoopStatement {
	/**
	 * The cached value of the '{@link #getCondition() <em>Condition</em>}' containment reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getCondition()
	 * @generated
	 * @ordered
	 */
	protected Expression condition;

	/**
	 * The cached value of the '{@link #getPostStatement() <em>Post Statement</em>}' containment reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getPostStatement()
	 * @generated
	 * @ordered
	 */
	protected Statement postStatement;

	/**
	 * The cached value of the '{@link #getInitStatement() <em>Init Statement</em>}' containment reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getInitStatement()
	 * @generated
	 * @ordered
	 */
	protected Statement initStatement;

	/**
	 * The cached value of the '{@link #getBodyStatement() <em>Body Statement</em>}' containment reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getBodyStatement()
	 * @generated
	 * @ordered
	 */
	protected Statement bodyStatement;

	/**
	 * The default value of the '{@link #getLoopType() <em>Loop Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getLoopType()
	 * @generated
	 * @ordered
	 */
	protected static final String LOOP_TYPE_EDEFAULT = null;

	/**
	 * The cached value of the '{@link #getLoopType() <em>Loop Type</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getLoopType()
	 * @generated
	 * @ordered
	 */
	protected String loopType = LOOP_TYPE_EDEFAULT;

	/**
	 * The default value of the '{@link #getControlVar() <em>Control Var</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getControlVar()
	 * @generated
	 * @ordered
	 */
	protected static final String CONTROL_VAR_EDEFAULT = null;

	/**
	 * The cached value of the '{@link #getControlVar() <em>Control Var</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getControlVar()
	 * @generated
	 * @ordered
	 */
	protected String controlVar = CONTROL_VAR_EDEFAULT;

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	protected LoopStatementImpl() {
		super();
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	protected EClass eStaticClass() {
		return DeslPackage.Literals.LOOP_STATEMENT;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Expression getCondition() {
		return condition;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public NotificationChain basicSetCondition(Expression newCondition, NotificationChain msgs) {
		Expression oldCondition = condition;
		condition = newCondition;
		if (eNotificationRequired()) {
			ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, DeslPackage.LOOP_STATEMENT__CONDITION, oldCondition, newCondition);
			if (msgs == null) msgs = notification; else msgs.add(notification);
		}
		return msgs;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setCondition(Expression newCondition) {
		if (newCondition != condition) {
			NotificationChain msgs = null;
			if (condition != null)
				msgs = ((InternalEObject)condition).eInverseRemove(this, EOPPOSITE_FEATURE_BASE - DeslPackage.LOOP_STATEMENT__CONDITION, null, msgs);
			if (newCondition != null)
				msgs = ((InternalEObject)newCondition).eInverseAdd(this, EOPPOSITE_FEATURE_BASE - DeslPackage.LOOP_STATEMENT__CONDITION, null, msgs);
			msgs = basicSetCondition(newCondition, msgs);
			if (msgs != null) msgs.dispatch();
		}
		else if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, DeslPackage.LOOP_STATEMENT__CONDITION, newCondition, newCondition));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Statement getPostStatement() {
		return postStatement;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public NotificationChain basicSetPostStatement(Statement newPostStatement, NotificationChain msgs) {
		Statement oldPostStatement = postStatement;
		postStatement = newPostStatement;
		if (eNotificationRequired()) {
			ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, DeslPackage.LOOP_STATEMENT__POST_STATEMENT, oldPostStatement, newPostStatement);
			if (msgs == null) msgs = notification; else msgs.add(notification);
		}
		return msgs;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setPostStatement(Statement newPostStatement) {
		if (newPostStatement != postStatement) {
			NotificationChain msgs = null;
			if (postStatement != null)
				msgs = ((InternalEObject)postStatement).eInverseRemove(this, EOPPOSITE_FEATURE_BASE - DeslPackage.LOOP_STATEMENT__POST_STATEMENT, null, msgs);
			if (newPostStatement != null)
				msgs = ((InternalEObject)newPostStatement).eInverseAdd(this, EOPPOSITE_FEATURE_BASE - DeslPackage.LOOP_STATEMENT__POST_STATEMENT, null, msgs);
			msgs = basicSetPostStatement(newPostStatement, msgs);
			if (msgs != null) msgs.dispatch();
		}
		else if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, DeslPackage.LOOP_STATEMENT__POST_STATEMENT, newPostStatement, newPostStatement));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Statement getInitStatement() {
		return initStatement;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public NotificationChain basicSetInitStatement(Statement newInitStatement, NotificationChain msgs) {
		Statement oldInitStatement = initStatement;
		initStatement = newInitStatement;
		if (eNotificationRequired()) {
			ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, DeslPackage.LOOP_STATEMENT__INIT_STATEMENT, oldInitStatement, newInitStatement);
			if (msgs == null) msgs = notification; else msgs.add(notification);
		}
		return msgs;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setInitStatement(Statement newInitStatement) {
		if (newInitStatement != initStatement) {
			NotificationChain msgs = null;
			if (initStatement != null)
				msgs = ((InternalEObject)initStatement).eInverseRemove(this, EOPPOSITE_FEATURE_BASE - DeslPackage.LOOP_STATEMENT__INIT_STATEMENT, null, msgs);
			if (newInitStatement != null)
				msgs = ((InternalEObject)newInitStatement).eInverseAdd(this, EOPPOSITE_FEATURE_BASE - DeslPackage.LOOP_STATEMENT__INIT_STATEMENT, null, msgs);
			msgs = basicSetInitStatement(newInitStatement, msgs);
			if (msgs != null) msgs.dispatch();
		}
		else if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, DeslPackage.LOOP_STATEMENT__INIT_STATEMENT, newInitStatement, newInitStatement));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Statement getBodyStatement() {
		return bodyStatement;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public NotificationChain basicSetBodyStatement(Statement newBodyStatement, NotificationChain msgs) {
		Statement oldBodyStatement = bodyStatement;
		bodyStatement = newBodyStatement;
		if (eNotificationRequired()) {
			ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, DeslPackage.LOOP_STATEMENT__BODY_STATEMENT, oldBodyStatement, newBodyStatement);
			if (msgs == null) msgs = notification; else msgs.add(notification);
		}
		return msgs;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setBodyStatement(Statement newBodyStatement) {
		if (newBodyStatement != bodyStatement) {
			NotificationChain msgs = null;
			if (bodyStatement != null)
				msgs = ((InternalEObject)bodyStatement).eInverseRemove(this, EOPPOSITE_FEATURE_BASE - DeslPackage.LOOP_STATEMENT__BODY_STATEMENT, null, msgs);
			if (newBodyStatement != null)
				msgs = ((InternalEObject)newBodyStatement).eInverseAdd(this, EOPPOSITE_FEATURE_BASE - DeslPackage.LOOP_STATEMENT__BODY_STATEMENT, null, msgs);
			msgs = basicSetBodyStatement(newBodyStatement, msgs);
			if (msgs != null) msgs.dispatch();
		}
		else if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, DeslPackage.LOOP_STATEMENT__BODY_STATEMENT, newBodyStatement, newBodyStatement));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public String getLoopType() {
		return loopType;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setLoopType(String newLoopType) {
		String oldLoopType = loopType;
		loopType = newLoopType;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, DeslPackage.LOOP_STATEMENT__LOOP_TYPE, oldLoopType, loopType));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public String getControlVar() {
		return controlVar;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setControlVar(String newControlVar) {
		String oldControlVar = controlVar;
		controlVar = newControlVar;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, DeslPackage.LOOP_STATEMENT__CONTROL_VAR, oldControlVar, controlVar));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) {
		switch (featureID) {
			case DeslPackage.LOOP_STATEMENT__CONDITION:
				return basicSetCondition(null, msgs);
			case DeslPackage.LOOP_STATEMENT__POST_STATEMENT:
				return basicSetPostStatement(null, msgs);
			case DeslPackage.LOOP_STATEMENT__INIT_STATEMENT:
				return basicSetInitStatement(null, msgs);
			case DeslPackage.LOOP_STATEMENT__BODY_STATEMENT:
				return basicSetBodyStatement(null, msgs);
		}
		return super.eInverseRemove(otherEnd, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public Object eGet(int featureID, boolean resolve, boolean coreType) {
		switch (featureID) {
			case DeslPackage.LOOP_STATEMENT__CONDITION:
				return getCondition();
			case DeslPackage.LOOP_STATEMENT__POST_STATEMENT:
				return getPostStatement();
			case DeslPackage.LOOP_STATEMENT__INIT_STATEMENT:
				return getInitStatement();
			case DeslPackage.LOOP_STATEMENT__BODY_STATEMENT:
				return getBodyStatement();
			case DeslPackage.LOOP_STATEMENT__LOOP_TYPE:
				return getLoopType();
			case DeslPackage.LOOP_STATEMENT__CONTROL_VAR:
				return getControlVar();
		}
		return super.eGet(featureID, resolve, coreType);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eSet(int featureID, Object newValue) {
		switch (featureID) {
			case DeslPackage.LOOP_STATEMENT__CONDITION:
				setCondition((Expression)newValue);
				return;
			case DeslPackage.LOOP_STATEMENT__POST_STATEMENT:
				setPostStatement((Statement)newValue);
				return;
			case DeslPackage.LOOP_STATEMENT__INIT_STATEMENT:
				setInitStatement((Statement)newValue);
				return;
			case DeslPackage.LOOP_STATEMENT__BODY_STATEMENT:
				setBodyStatement((Statement)newValue);
				return;
			case DeslPackage.LOOP_STATEMENT__LOOP_TYPE:
				setLoopType((String)newValue);
				return;
			case DeslPackage.LOOP_STATEMENT__CONTROL_VAR:
				setControlVar((String)newValue);
				return;
		}
		super.eSet(featureID, newValue);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eUnset(int featureID) {
		switch (featureID) {
			case DeslPackage.LOOP_STATEMENT__CONDITION:
				setCondition((Expression)null);
				return;
			case DeslPackage.LOOP_STATEMENT__POST_STATEMENT:
				setPostStatement((Statement)null);
				return;
			case DeslPackage.LOOP_STATEMENT__INIT_STATEMENT:
				setInitStatement((Statement)null);
				return;
			case DeslPackage.LOOP_STATEMENT__BODY_STATEMENT:
				setBodyStatement((Statement)null);
				return;
			case DeslPackage.LOOP_STATEMENT__LOOP_TYPE:
				setLoopType(LOOP_TYPE_EDEFAULT);
				return;
			case DeslPackage.LOOP_STATEMENT__CONTROL_VAR:
				setControlVar(CONTROL_VAR_EDEFAULT);
				return;
		}
		super.eUnset(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public boolean eIsSet(int featureID) {
		switch (featureID) {
			case DeslPackage.LOOP_STATEMENT__CONDITION:
				return condition != null;
			case DeslPackage.LOOP_STATEMENT__POST_STATEMENT:
				return postStatement != null;
			case DeslPackage.LOOP_STATEMENT__INIT_STATEMENT:
				return initStatement != null;
			case DeslPackage.LOOP_STATEMENT__BODY_STATEMENT:
				return bodyStatement != null;
			case DeslPackage.LOOP_STATEMENT__LOOP_TYPE:
				return LOOP_TYPE_EDEFAULT == null ? loopType != null : !LOOP_TYPE_EDEFAULT.equals(loopType);
			case DeslPackage.LOOP_STATEMENT__CONTROL_VAR:
				return CONTROL_VAR_EDEFAULT == null ? controlVar != null : !CONTROL_VAR_EDEFAULT.equals(controlVar);
		}
		return super.eIsSet(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public String toString() {
		if (eIsProxy()) return super.toString();

		StringBuffer result = new StringBuffer(super.toString());
		result.append(" (loopType: ");
		result.append(loopType);
		result.append(", controlVar: ");
		result.append(controlVar);
		result.append(')');
		return result.toString();
	}

	@Override
	public Object execute(IExecutionContext context) {
		IExecutionContext childContext = context.createChildContext();
		if ("for".equals(getLoopType()))
			return forLoop(childContext);
		if("foreach".equals(getLoopType()))
			return forEachLoop(childContext);
		if ("while".equals(getLoopType()))
			return whileLoop(childContext);
		return null;
	}

	private Object forLoop(IExecutionContext context) {
		getInitStatement().execute(context);
		while((Boolean)context.resolveValue(getCondition())) {
			Object res = getBodyStatement().execute(context);
			if (res instanceof Statement) {
				Statement s = (Statement) res;
				if ("break".equals(s.getName()))
					break;
				if ("return".equals(s.getName()))
					return s;
			}
			getPostStatement().execute(context);
		}
		return null;
	}

	@SuppressWarnings("rawtypes")
	private Object forEachLoop(IExecutionContext context) {
		Object list = context.resolveValue(getCondition());
		if (list instanceof Iterable) {	
			IVariable controlVar = context.createVariable(getControlVar(), null);
			for(Object item: (Iterable)list) {
				controlVar.setValue(item);
				getBodyStatement().execute(context);
			}	
		}
		else if (list instanceof Object[]) {
			IVariable controlVar = context.createVariable(getControlVar(), null);
			for (int i = 0; i < Array.getLength(list); i++) {
				controlVar.setValue(Array.get(list, i));
				getBodyStatement().execute(context);
			}
		}
		return null;
	}

	private Object whileLoop(IExecutionContext context) {
		while((Boolean)context.resolveValue(getCondition())) {
			Object res = getBodyStatement().execute(context);
			if (res instanceof Statement) {
				Statement s = (Statement) res;
				if ("break".equals(s.getName()))
					break;
				if ("return".equals(s.getName()))
					return s;
			}
		}
		return null;
	}
} //LoopStatementImpl
